#include "./nozzle.h"
#include "../xlib/gpio.h"
#include "../xlib/pwm.h"
#include "./step_motor_drv.h"

namespace driver {

Nozzle nozzle;

using namespace stm32f1;

namespace nozzle_impl {

using namespace gpio;

using PULS = Cfg<PA<2>, Mode::OUT_50MHz_AF_PP>; // TIM5_CH3
using DIR = Cfg<PA<3>, Mode::OUT_50MHz_PP>;
using ENABLE = Cfg<PA<4>, Mode::OUT_50MHz_PP>;
using ALM = Cfg<PB<15>, Mode::INPUT_FLOAT>;
using PWM = pwm::Timer5PWM<3>;
using ORIGIN_UP = Cfg<PB<14>, Mode::INPUT_FLOAT>;
using ORIGIN_BOTTOM = Cfg<PB<13>, Mode::INPUT_FLOAT>;

struct NozzleCfg {
    static void set_dir(SimpleDir dir) {
        switch (dir) {
        case SimpleDir::ENTER:
            DIR::clr();
            break;
        case SimpleDir::EXIT:
            DIR::set();
            break;
        case SimpleDir::DIRECT:
            DIR::set();
            break;
        }
    }
    static void set_pwm_freq(uint32_t freq) { PWM::set_freq(freq); }
    static void enable_pwm(uint32_t freq) {
        PULS::set_mode<Mode::OUT_50MHz_AF_PP>();
        PWM::start_with_irq(freq);
    }
    static void enable_pwm_not_irq(uint32_t freq) {
        PULS::set_mode<Mode::OUT_50MHz_AF_PP>();
        PWM::start_with_no_irq(freq);
    }
    static void disable_pwm() {
        PULS::set_mode<Mode::OUT_50MHz_PP>();
        PWM::stop();
    }
    static bool is_origin() { return !ORIGIN_UP::read(); }
    static bool is_alm() { return !ALM::read(); }

    static constexpr uint32_t BASE_FREQ = 500;
    static constexpr uint32_t ACC = 2;
    static constexpr uint32_t VAR_RANGE = 2000;

    static constexpr uint8_t EC_ALM = utils::ec::NOZZLE_ALM;
    static constexpr uint8_t EC_TIMEOUT = utils::ec::NOZZLE_TIMEOUT;

    static constexpr uint32_t RESET_PULS_COUNT = 500;
    static constexpr uint32_t RESET_TIMEOUT = 30 * 1000;
    static constexpr uint32_t RESET_FREQ = 2000;
    static constexpr uint32_t SLOW_DELAY_MS = 1;
    using PULS_PIN = PULS;
};

static StepMotorDrv<NozzleCfg> drv;

} // namespace nozzle_impl

using namespace nozzle_impl;

void Nozzle::init() {
    rcc::enable<PULS, DIR, ENABLE, ALM, PWM, ORIGIN_BOTTOM, ORIGIN_UP>();
    gpio::config<PULS, DIR, ENABLE, ALM, ORIGIN_BOTTOM, ORIGIN_UP>();
    drv.init();
    ENABLE::clr();
    NVIC_EnableIRQ(TIM5_IRQn);
}

bool Nozzle::is_alm() const { return !ALM::read(); }
bool Nozzle::is_origin() const { return !ORIGIN_UP::read(); }
bool Nozzle::is_origin2() const { return !ORIGIN_BOTTOM::read(); }
utils::Error Nozzle::run(uint16_t mm) {
    if (mm == 0) {
        return drv.reset();
    }
    return drv.run_target(mm * 400 / 4);
}

} // namespace driver

using namespace driver;

extern "C" void TIM5_IRQHandler() {
    if (PWM::is_uif()) {
        PWM::clear_flag();
        drv.on_puls();
    }
}
